0208. 实现 Trie (前缀树)【中等】
1. 📝 题目描述
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()初始化前缀树对象。void insert(String word)向前缀树中插入字符串word。boolean search(String word)如果字符串word在前缀树中,返回true(即,在检索之前已经插入);否则,返回false。boolean startsWith(String prefix)如果之前已经插入的字符串word的前缀之一为prefix,返回true;否则,返回false。
示例:
txt
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
提示:
1 <= word.length, prefix.length <= 2000word和prefix仅由小写英文字母组成insert、search和startsWith调用次数 总计 不超过3 * 10^4次
2. 🎯 s.1 - 字典树
c
typedef struct Trie {
struct Trie* children[26];
bool isEnd;
} Trie;
Trie* trieCreate() {
Trie* node = (Trie*)calloc(1, sizeof(Trie));
return node;
}
void trieInsert(Trie* obj, char* word) {
for (int i = 0; word[i]; i++) {
int idx = word[i] - 'a';
if (!obj->children[idx]) obj->children[idx] = trieCreate();
obj = obj->children[idx];
}
obj->isEnd = true;
}
bool trieSearch(Trie* obj, char* word) {
for (int i = 0; word[i]; i++) {
int idx = word[i] - 'a';
if (!obj->children[idx]) return false;
obj = obj->children[idx];
}
return obj->isEnd;
}
bool trieStartsWith(Trie* obj, char* prefix) {
for (int i = 0; prefix[i]; i++) {
int idx = prefix[i] - 'a';
if (!obj->children[idx]) return false;
obj = obj->children[idx];
}
return true;
}
void trieFree(Trie* obj) {
for (int i = 0; i < 26; i++) {
if (obj->children[i]) trieFree(obj->children[i]);
}
free(obj);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
js
var Trie = function () {
this.children = {}
this.isEnd = false
}
/**
* @param {string} word
* @return {void}
*/
Trie.prototype.insert = function (word) {
let node = this
for (const ch of word) {
if (!node.children[ch]) node.children[ch] = new Trie()
node = node.children[ch]
}
node.isEnd = true
}
/**
* @param {string} word
* @return {boolean}
*/
Trie.prototype.search = function (word) {
const node = this._searchPrefix(word)
return node !== null && node.isEnd
}
/**
* @param {string} prefix
* @return {boolean}
*/
Trie.prototype.startsWith = function (prefix) {
return this._searchPrefix(prefix) !== null
}
Trie.prototype._searchPrefix = function (prefix) {
let node = this
for (const ch of prefix) {
if (!node.children[ch]) return null
node = node.children[ch]
}
return node
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
py
class Trie:
def __init__(self):
self.children = {}
self.is_end = False
def insert(self, word: str) -> None:
node = self
for ch in word:
if ch not in node.children:
node.children[ch] = Trie()
node = node.children[ch]
node.is_end = True
def search(self, word: str) -> bool:
node = self._search_prefix(word)
return node is not None and node.is_end
def startsWith(self, prefix: str) -> bool:
return self._search_prefix(prefix) is not None
def _search_prefix(self, prefix: str):
node = self
for ch in prefix:
if ch not in node.children:
return None
node = node.children[ch]
return node1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 时间复杂度:
insert/search/startsWith均为 ,其中 是单词或前缀的长度 - 空间复杂度:
,其中 是所有插入单词的字符总数
算法思路:
- 每个节点包含子节点映射和是否为单词结尾的标记
insert:逐字符遍历,不存在则创建新节点,最后标记结尾search和startsWith复用前缀搜索逻辑,区别在于是否检查isEnd